/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "gpio.h"
#include "sys.h"
#include "usart.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ds18b20.h"
#include "serial.h"
#include "subctrl.h"
#include <stdint.h>

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

log_level_t log_level = kLogInfo;

sub_control_t *sub_ctrl = NULL;
serial_handle_t log_handle = NULL;
serial_handle_t rs485_handle = NULL;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

void Serial_RecvCallback(serial_handle_t handle);
void RS485_RecvCallback(serial_handle_t handle);
void RS485Comm_Send(uint8_t *data, uint16_t size);
void Serial_SendCallback(serial_handle_t handle, uint8_t *data, uint16_t size);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{

    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_USART1_UART_Init();
    MX_USART3_UART_Init();
    /* USER CODE BEGIN 2 */
    log_handle = Serial_Init(&UART_HANDLE_LOG, Serial_RecvCallback, Serial_SendCallback);
    rs485_handle = Serial_Init(&UART_HANDLE_RS485, RS485_RecvCallback, Serial_SendCallback);
    sub_ctrl = SubCtrl_Init();

    HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_LOG, log_handle->payload, SERIAL_PAYLOAD_SIZE);
    HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_RS485, rs485_handle->payload, SERIAL_PAYLOAD_SIZE);
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        Serial_Run(log_handle);
        Serial_Run(rs485_handle);

        DS18B20_Run();
        SubCtrl_Run();
    }
    /* USER CODE END 3 */
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler();
    }
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
        Error_Handler();
    }
}

/**
16位进制
调试：
    设置地址：35 02 C0A809（65~6a)
    查询报文：35 00
    打开 P D：350100000001
    设置功率：35 01  （4字节）
        设置500 ：35010000FA43
        设置1000：350100007A44
        设置1200：350100009644
        设置1500：35010080BB44
    保存参数：35 03
内部通讯：
    设置地址：34 地址 02 （65~6a)
    查询报文：34 地址 00
    设置功率：34 地址 01  （4字节）
        34 E9 01  （4字节）
    保存参数：34 地址 03
        34 E9 03
**/
/* USER CODE BEGIN 4 */
void Serial_RecvCallback(serial_handle_t handle)
{
    if (handle->payload[0] == SERIAL_FRAME_HEADER)
    {
        if (ADD8_Check(handle->payload, handle->size))
        {
            LOGI("Check error!");
            goto LOG_END;
        }
        switch (handle->payload[1])
        {
        case kSerialCmdQuery: /* 查询报文 */
            // clang-format off
            LOGI("\n<------------ Compilation date %s ------------>\n"
                 "IP: %d.%d.%d.%d\n"
                 "Output:  %s\n"
                 "Power:   %7.2fW\n"
                 "Forward: %7.2fW\n"
                 "Reverse: %7.4fW\n"
                 "Current: %5.2f  %5.2f  %5.2f  %5.2f  %5.2f  %5.2f  %5.2f\n"
                 "Temp:    %5.2f  %5.2f  %5.2f  %5.2f  %5.2f  %5.2f  %5.2f",
                 __DATE__,
                 sub_ctrl->header.ip[0], sub_ctrl->header.ip[1], sub_ctrl->header.ip[2], sub_ctrl->header.ip[3],
                 sub_ctrl->state.pa_enable ? "ON" : "OFF",
                 sub_ctrl->rf_power.target,
                 sub_ctrl->rf_power.forward,
                 sub_ctrl->rf_power.reverse,
                 sub_ctrl->pa[0].current,
                 sub_ctrl->pa[1].current,
                 sub_ctrl->pa[2].current,
                 sub_ctrl->pa[3].current,
                 sub_ctrl->pa[4].current,
                 sub_ctrl->pa[5].current,
                 sub_ctrl->pa[6].current,
                 sub_ctrl->pa[0].temperature,
                 sub_ctrl->pa[1].temperature,
                 sub_ctrl->pa[2].temperature,
                 sub_ctrl->pa[3].temperature,
                 sub_ctrl->pa[4].temperature,
                 sub_ctrl->pa[5].temperature,
                 sub_ctrl->pa[6].temperature
            );
            // clang-format on
            break;
        case kSerialCmdReset: /* 复位 */
            LOGI("System reset!");
            HAL_Delay(100);
            System_Reset();
            break;
        case kSerialCmdSetPower: /* 设定功率 */
            SubCtrl_SetPower(DataInversion(&handle->payload[2]).integer);
            break;
        case kSerialCmdSetAddress: /* 设定地址 */
            SubCtrl_SetAddress(&handle->payload[2]);
            break;
        case kSerialCmdSaveParameter: /* 保存参数 */
            SubCtrl_SaveParams();
            break;
        case kSerialCmdSetForwardPoint: /* 设定正向功率点 */
            SubCtrl_SetForwardPoint(*(uint8_t *)&handle->payload[2], DataInversion(&handle->payload[3]).integer);
            break;
        case kSerialCmdSetForwardCalibration: /* 设置正向功率校准 */
            SubCtrl_SetForwardCalibration(
                DataInversion(&handle->payload[2]).decimal, DataInversion(&handle->payload[6]).decimal,
                DataInversion(&handle->payload[10]).decimal, DataInversion(&handle->payload[14]).decimal);
            break;
        case kSerialCmdGetForwardCalibration: /* 获取正向功率校准 */
            SubCtrl_GetForwardCalibration();
            break;
        case kSerialCmdStartForwardCalibration: /* 开始正向功率校准 */
            SubCtrl_StartForwardCalibration();
            break;
        case kSerialCmdResetForwardCalibration: /* 重置正向功率校准 */
            SubCtrl_ResetForwardCalibration();
            break;
        case kSerialCmdSetMaxForwardPower: /* 设置最大正向功率 */
            SubCtrl_SetMaxForwardPower(DataInversion(&handle->payload[2]).integer);
            break;
        default:
            LOGE("Unknown command: 0x%02X", handle->payload[1]);
            break;
        }
    }
    else
    {
        LOGE("Frame header error!");
    }
LOG_END:
    HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_LOG, log_handle->payload, SERIAL_PAYLOAD_SIZE);
}

void RS485_RecvCallback(serial_handle_t handle)
{
    if (handle->payload[0] == RS485_FRAME_HEADER)
    {
        if (ADD8_Check(handle->payload, handle->size)) // 校验错误
            goto RS485_END;
        if (Subctrl_CheckAddr(handle->payload[1])) // IP地址匹配
        {
            switch (handle->payload[2])
            {
            case kRS485CmdQuery: /* 查询报文 */
                ADD8_Calculation((uint8_t *)sub_ctrl, sizeof(sub_control_t) - 1);
                RS485Comm_Send((uint8_t *)sub_ctrl, sizeof(sub_control_t));
                break;
            case kRS485CmdSaveParameter: /* 保存参数 */
                SubCtrl_SaveParams();

                break;
            case kRS485CmdSetPower: /* 设定功率 */
                SubCtrl_SetPower(*(int *)&handle->payload[3]);
                break;
            }
        }
        else if (handle->payload[1] == SUBCTRL_BASE_ADDRESS + kSubCtrlNum) // 通用地址
        {
            switch (handle->payload[2])
            {
            case kRS485CmdSaveParameter: /* 保存参数 */
                SubCtrl_SaveParams();
                break;
            case kRS485CmdSetPower: /* 设定功率 */
                SubCtrl_SetPower(*(int *)&handle->payload[3]);
                break;
            }
        }
    }
RS485_END:
    HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_RS485, rs485_handle->payload, SERIAL_PAYLOAD_SIZE);
}

void Serial_SendCallback(serial_handle_t handle, uint8_t *data, uint16_t size)
{
    HAL_UART_Transmit_DMA(handle->arg, data, size);
}

inline void RS485Comm_Send(uint8_t *data, uint16_t size)
{
    Serial_Print(rs485_handle, data, size);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    LED_COMM_ON();
    if (huart == &UART_HANDLE_LOG)
    {
        Serial_RecvComplete(log_handle, Size);
    }
    else if (huart == &UART_HANDLE_RS485)
    {
        Serial_RecvComplete(rs485_handle, Size);
    }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    LED_COMM_OFF();
    if (huart == &UART_HANDLE_LOG)
    {
        Serial_SendComplete(log_handle);
    }
    else if (huart == &UART_HANDLE_RS485)
    {
        Serial_SendComplete(rs485_handle);
    }
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    if (huart == &UART_HANDLE_LOG)
    {
        HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_LOG, log_handle->payload, SERIAL_PAYLOAD_SIZE);
    }
    else if (huart == &UART_HANDLE_RS485)
    {
        HAL_UARTEx_ReceiveToIdle_DMA(&UART_HANDLE_RS485, rs485_handle->payload, SERIAL_PAYLOAD_SIZE);
    }
}
/* USER CODE END 4 */

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
